import { computed, ReadonlySignal, Signal, signal } from '@preact/signals-core'
import {
  isDarkMode,
  readReactive,
  withOpacity,
  componentDefaults as baseComponentDefaults,
  imageDefaults as baseImageDefaults,
  inputDefaults as baseInputDefaults,
  textDefaults as baseTextDefaults,
  contentDefaults as baseContentDefaults,
} from '@pmndrs/uikit'
import { Color, ColorRepresentation } from 'three'

function hsl(h: number, s: number, l: number): Signal<ColorRepresentation> | ColorRepresentation {
  return new Color().setHSL(h / 360, s / 100, l / 100, 'srgb')
}

const defaultTheme = {
  radius: 8 as const,
  background: hsl(0, 0, 100),
  foreground: hsl(0, 0, 3.9),
  card: hsl(0, 0, 100),
  cardForeground: hsl(0, 0, 3.9),
  popover: hsl(0, 0, 100),
  popoverForeground: hsl(0, 0, 3.9),
  primary: hsl(0, 0, 9),
  primaryForeground: hsl(0, 0, 98),
  secondary: hsl(0, 0, 96.1),
  secondaryForeground: hsl(0, 0, 9),
  muted: hsl(0, 0, 96.1),
  mutedForeground: hsl(0, 0, 45.1),
  accent: hsl(0, 0, 96.1),
  accentForeground: hsl(0, 0, 9),
  destructive: hsl(0, 84.2, 60.2),
  destructiveForeground: hsl(0, 0, 98),
  border: hsl(0, 0, 89.8),
  input: hsl(0, 0, 89.8),
  ring: hsl(0, 0, 3.9),
  dark: {
    background: hsl(0, 0, 3.9),
    foreground: hsl(0, 0, 98),
    card: hsl(0, 0, 3.9),
    cardForeground: hsl(0, 0, 98),
    popover: hsl(0, 0, 3.9),
    popoverForeground: hsl(0, 0, 98),
    primary: hsl(0, 0, 98),
    primaryForeground: hsl(0, 0, 9),
    secondary: hsl(0, 0, 14.9),
    secondaryForeground: hsl(0, 0, 98),
    muted: hsl(0, 0, 14.9),
    mutedForeground: hsl(0, 0, 63.9),
    accent: hsl(0, 0, 14.9),
    accentForeground: hsl(0, 0, 98),
    destructive: hsl(0, 62.8, 30.6),
    destructiveForeground: hsl(0, 0, 98),
    border: hsl(0, 0, 14.9),
    input: hsl(0, 0, 14.9),
    ring: hsl(0, 0, 83.1),
  },
} as const

export type Theme = Omit<typeof defaultTheme, 'dark'> & { dark?: Partial<Omit<typeof defaultTheme, 'dark'>> }

export const defaultThemes = {
  default: defaultTheme,
  red: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(0, 0, 3.9),
    card: hsl(0, 0, 100),
    cardForeground: hsl(0, 0, 3.9),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(0, 0, 3.9),
    primary: hsl(0, 72.2, 50.6),
    primaryForeground: hsl(0, 85.7, 97.3),
    secondary: hsl(0, 0, 96.1),
    secondaryForeground: hsl(0, 0, 9),
    muted: hsl(0, 0, 96.1),
    mutedForeground: hsl(0, 0, 45.1),
    accent: hsl(0, 0, 96.1),
    accentForeground: hsl(0, 0, 9),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(0, 0, 98),
    border: hsl(0, 0, 89.8),
    input: hsl(0, 0, 89.8),
    ring: hsl(0, 72.2, 50.6),
    dark: {
      background: hsl(0, 0, 3.9),
      foreground: hsl(0, 0, 98),
      card: hsl(0, 0, 3.9),
      cardForeground: hsl(0, 0, 98),
      popover: hsl(0, 0, 3.9),
      popoverForeground: hsl(0, 0, 98),
      primary: hsl(0, 72.2, 50.6),
      primaryForeground: hsl(0, 85.7, 97.3),
      secondary: hsl(0, 0, 14.9),
      secondaryForeground: hsl(0, 0, 98),
      muted: hsl(0, 0, 14.9),
      mutedForeground: hsl(0, 0, 63.9),
      accent: hsl(0, 0, 14.9),
      accentForeground: hsl(0, 0, 98),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(0, 0, 98),
      border: hsl(0, 0, 14.9),
      input: hsl(0, 0, 14.9),
      ring: hsl(0, 72.2, 50.6),
    },
  },
  rose: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(240, 10, 3.9),
    card: hsl(0, 0, 100),
    cardForeground: hsl(240, 10, 3.9),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(240, 10, 3.9),
    primary: hsl(346.8, 77.2, 49.8),
    primaryForeground: hsl(355.7, 100, 97.3),
    secondary: hsl(240, 4.8, 95.9),
    secondaryForeground: hsl(240, 5.9, 10),
    muted: hsl(240, 4.8, 95.9),
    mutedForeground: hsl(240, 3.8, 46.1),
    accent: hsl(240, 4.8, 95.9),
    accentForeground: hsl(240, 5.9, 10),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(0, 0, 98),
    border: hsl(240, 5.9, 90),
    input: hsl(240, 5.9, 90),
    ring: hsl(346.8, 77.2, 49.8),
    dark: {
      background: hsl(20, 14.3, 4.1),
      foreground: hsl(0, 0, 95),
      popover: hsl(0, 0, 9),
      popoverForeground: hsl(0, 0, 95),
      card: hsl(24, 9.8, 10),
      cardForeground: hsl(0, 0, 95),
      primary: hsl(346.8, 77.2, 49.8),
      primaryForeground: hsl(355.7, 100, 97.3),
      secondary: hsl(240, 3.7, 15.9),
      secondaryForeground: hsl(0, 0, 98),
      muted: hsl(0, 0, 15),
      mutedForeground: hsl(240, 5, 64.9),
      accent: hsl(12, 6.5, 15.1),
      accentForeground: hsl(0, 0, 98),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(0, 85.7, 97.3),
      border: hsl(240, 3.7, 15.9),
      input: hsl(240, 3.7, 15.9),
      ring: hsl(346.8, 77.2, 49.8),
    },
  },
  orange: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(20, 14.3, 4.1),
    card: hsl(0, 0, 100),
    cardForeground: hsl(20, 14.3, 4.1),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(20, 14.3, 4.1),
    primary: hsl(24.6, 95, 53.1),
    primaryForeground: hsl(60, 9.1, 97.8),
    secondary: hsl(60, 4.8, 95.9),
    secondaryForeground: hsl(24, 9.8, 10),
    muted: hsl(60, 4.8, 95.9),
    mutedForeground: hsl(25, 5.3, 44.7),
    accent: hsl(60, 4.8, 95.9),
    accentForeground: hsl(24, 9.8, 10),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(60, 9.1, 97.8),
    border: hsl(20, 5.9, 90),
    input: hsl(20, 5.9, 90),
    ring: hsl(24.6, 95, 53.1),
    dark: {
      background: hsl(20, 14.3, 4.1),
      foreground: hsl(60, 9.1, 97.8),
      card: hsl(20, 14.3, 4.1),
      cardForeground: hsl(60, 9.1, 97.8),
      popover: hsl(20, 14.3, 4.1),
      popoverForeground: hsl(60, 9.1, 97.8),
      primary: hsl(20.5, 90.2, 48.2),
      primaryForeground: hsl(60, 9.1, 97.8),
      secondary: hsl(12, 6.5, 15.1),
      secondaryForeground: hsl(60, 9.1, 97.8),
      muted: hsl(12, 6.5, 15.1),
      mutedForeground: hsl(24, 5.4, 63.9),
      accent: hsl(12, 6.5, 15.1),
      accentForeground: hsl(60, 9.1, 97.8),
      destructive: hsl(0, 72.2, 50.6),
      destructiveForeground: hsl(60, 9.1, 97.8),
      border: hsl(12, 6.5, 15.1),
      input: hsl(12, 6.5, 15.1),
      ring: hsl(20.5, 90.2, 48.2),
    },
  },
  green: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(240, 10, 3.9),
    card: hsl(0, 0, 100),
    cardForeground: hsl(240, 10, 3.9),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(240, 10, 3.9),
    primary: hsl(142.1, 76.2, 36.3),
    primaryForeground: hsl(355.7, 100, 97.3),
    secondary: hsl(240, 4.8, 95.9),
    secondaryForeground: hsl(240, 5.9, 10),
    muted: hsl(240, 4.8, 95.9),
    mutedForeground: hsl(240, 3.8, 46.1),
    accent: hsl(240, 4.8, 95.9),
    accentForeground: hsl(240, 5.9, 10),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(0, 0, 98),
    border: hsl(240, 5.9, 90),
    input: hsl(240, 5.9, 90),
    ring: hsl(142.1, 76.2, 36.3),
    dark: {
      background: hsl(20, 14.3, 4.1),
      foreground: hsl(0, 0, 95),
      popover: hsl(0, 0, 9),
      popoverForeground: hsl(0, 0, 95),
      card: hsl(24, 9.8, 10),
      cardForeground: hsl(0, 0, 95),
      primary: hsl(142.1, 70.6, 45.3),
      primaryForeground: hsl(144.9, 80.4, 10),
      secondary: hsl(240, 3.7, 15.9),
      secondaryForeground: hsl(0, 0, 98),
      muted: hsl(0, 0, 15),
      mutedForeground: hsl(240, 5, 64.9),
      accent: hsl(12, 6.5, 15.1),
      accentForeground: hsl(0, 0, 98),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(0, 85.7, 97.3),
      border: hsl(240, 3.7, 15.9),
      input: hsl(240, 3.7, 15.9),
      ring: hsl(142.4, 71.8, 29.2),
    },
  },
  blue: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(222.2, 84, 4.9),
    card: hsl(0, 0, 100),
    cardForeground: hsl(222.2, 84, 4.9),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(222.2, 84, 4.9),
    primary: hsl(221.2, 83.2, 53.3),
    primaryForeground: hsl(210, 40, 98),
    secondary: hsl(210, 40, 96.1),
    secondaryForeground: hsl(222.2, 47.4, 11.2),
    muted: hsl(210, 40, 96.1),
    mutedForeground: hsl(215.4, 16.3, 46.9),
    accent: hsl(210, 40, 96.1),
    accentForeground: hsl(222.2, 47.4, 11.2),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(210, 40, 98),
    border: hsl(214.3, 31.8, 91.4),
    input: hsl(214.3, 31.8, 91.4),
    ring: hsl(221.2, 83.2, 53.3),
    dark: {
      background: hsl(222.2, 84, 4.9),
      foreground: hsl(210, 40, 98),
      card: hsl(222.2, 84, 4.9),
      cardForeground: hsl(210, 40, 98),
      popover: hsl(222.2, 84, 4.9),
      popoverForeground: hsl(210, 40, 98),
      primary: hsl(217.2, 91.2, 59.8),
      primaryForeground: hsl(222.2, 47.4, 11.2),
      secondary: hsl(217.2, 32.6, 17.5),
      secondaryForeground: hsl(210, 40, 98),
      muted: hsl(217.2, 32.6, 17.5),
      mutedForeground: hsl(215, 20.2, 65.1),
      accent: hsl(217.2, 32.6, 17.5),
      accentForeground: hsl(210, 40, 98),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(210, 40, 98),
      border: hsl(217.2, 32.6, 17.5),
      input: hsl(217.2, 32.6, 17.5),
      ring: hsl(224.3, 76.3, 48),
    },
  },
  yellow: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(20, 14.3, 4.1),
    card: hsl(0, 0, 100),
    cardForeground: hsl(20, 14.3, 4.1),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(20, 14.3, 4.1),
    primary: hsl(47.9, 95.8, 53.1),
    primaryForeground: hsl(26, 83.3, 14.1),
    secondary: hsl(60, 4.8, 95.9),
    secondaryForeground: hsl(24, 9.8, 10),
    muted: hsl(60, 4.8, 95.9),
    mutedForeground: hsl(25, 5.3, 44.7),
    accent: hsl(60, 4.8, 95.9),
    accentForeground: hsl(24, 9.8, 10),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(60, 9.1, 97.8),
    border: hsl(20, 5.9, 90),
    input: hsl(20, 5.9, 90),
    ring: hsl(20, 14.3, 4.1),
    dark: {
      background: hsl(20, 14.3, 4.1),
      foreground: hsl(60, 9.1, 97.8),
      card: hsl(20, 14.3, 4.1),
      cardForeground: hsl(60, 9.1, 97.8),
      popover: hsl(20, 14.3, 4.1),
      popoverForeground: hsl(60, 9.1, 97.8),
      primary: hsl(47.9, 95.8, 53.1),
      primaryForeground: hsl(26, 83.3, 14.1),
      secondary: hsl(12, 6.5, 15.1),
      secondaryForeground: hsl(60, 9.1, 97.8),
      muted: hsl(12, 6.5, 15.1),
      mutedForeground: hsl(24, 5.4, 63.9),
      accent: hsl(12, 6.5, 15.1),
      accentForeground: hsl(60, 9.1, 97.8),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(60, 9.1, 97.8),
      border: hsl(12, 6.5, 15.1),
      input: hsl(12, 6.5, 15.1),
      ring: hsl(35.5, 91.7, 32.9),
    },
  },
  violet: {
    radius: 8,
    background: hsl(0, 0, 100),
    foreground: hsl(224, 71.4, 4.1),
    card: hsl(0, 0, 100),
    cardForeground: hsl(224, 71.4, 4.1),
    popover: hsl(0, 0, 100),
    popoverForeground: hsl(224, 71.4, 4.1),
    primary: hsl(262.1, 83.3, 57.8),
    primaryForeground: hsl(210, 20, 98),
    secondary: hsl(220, 14.3, 95.9),
    secondaryForeground: hsl(220.9, 39.3, 11),
    muted: hsl(220, 14.3, 95.9),
    mutedForeground: hsl(220, 8.9, 46.1),
    accent: hsl(220, 14.3, 95.9),
    accentForeground: hsl(220.9, 39.3, 11),
    destructive: hsl(0, 84.2, 60.2),
    destructiveForeground: hsl(210, 20, 98),
    border: hsl(220, 13, 91),
    input: hsl(220, 13, 91),
    ring: hsl(262.1, 83.3, 57.8),
    dark: {
      background: hsl(224, 71.4, 4.1),
      foreground: hsl(210, 20, 98),
      card: hsl(224, 71.4, 4.1),
      cardForeground: hsl(210, 20, 98),
      popover: hsl(224, 71.4, 4.1),
      popoverForeground: hsl(210, 20, 98),
      primary: hsl(263.4, 70, 50.4),
      primaryForeground: hsl(210, 20, 98),
      secondary: hsl(215, 27.9, 16.9),
      secondaryForeground: hsl(210, 20, 98),
      muted: hsl(215, 27.9, 16.9),
      mutedForeground: hsl(217.9, 10.6, 64.9),
      accent: hsl(215, 27.9, 16.9),
      accentForeground: hsl(210, 20, 98),
      destructive: hsl(0, 62.8, 30.6),
      destructiveForeground: hsl(210, 20, 98),
      border: hsl(215, 27.9, 16.9),
      input: hsl(215, 27.9, 16.9),
      ring: hsl(263.4, 70, 50.4),
    },
  },
} as const satisfies Record<string, Theme>

const baseBorderRadius = computed(() => computeThemeProperty('radius'))

export const borderRadius = {
  lg: baseBorderRadius,
  md: computed(() => baseBorderRadius.value - 2),
  sm: computed(() => baseBorderRadius.value - 4),
}

export const theme = signal<Theme>(defaultThemes.default)

export function setTheme(newTheme: Theme) {
  theme.value = newTheme
}

export type ReadReactive<T> = T extends ReadonlySignal<infer K> ? K : T

function computeThemeProperty<T extends keyof Omit<Theme, 'dark'>>(key: T) {
  return ((isDarkMode.value ? readReactive(theme.value.dark?.[key]) : undefined) ??
    readReactive(theme.value[key])) as ReadReactive<Theme[T]>
}

export const colors = {} as {
  -readonly [Key in keyof Omit<Theme, 'dark' | 'radius'>]: ReadonlySignal<ColorRepresentation>
}
for (const anyKey in defaultThemes['default']) {
  const key = anyKey as keyof (typeof defaultThemes)['default']
  if (key === 'radius' || key === 'dark') {
    continue
  }
  colors[key] = computed(() => computeThemeProperty(key))
}

export const defaults = {
  scrollbarColor: withOpacity(colors.foreground, 0.3),
  scrollbarBorderRadius: 4,
  lineHeight: '150%',
  borderColor: colors.border,
  color: colors.foreground,
} as const

export const componentDefaults = {
  ...baseComponentDefaults,
  ...defaults,
} as const

export const inputDefaults = {
  ...baseInputDefaults,
  ...defaults,
} as const

export const textDefaults = {
  ...baseTextDefaults,
  ...defaults,
}

export const imageDefaults = {
  ...baseImageDefaults,
  ...defaults,
} as const

export const contentDefaults = {
  ...baseContentDefaults,
  ...defaults,
} as const
